package com.qyb.base.java8.chap5;



import com.qyb.base.java8.bean.Dish;

import java.util.Arrays;
import java.util.List;
import java.util.OptionalInt;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static com.qyb.base.java8.bean.Dish.menu;


public class NumericStreams{

    /*
     原始类型流特化
        Java 8引入了三个原始类型特化流接口来解决这个问题：IntStream、DoubleStream和
        LongStream，分别将流中的元素特化为int、long和double，从而避免了暗含的装箱成本。每
        个接口都带来了进行常用数值归约的新方法，比如对数值流求和的sum，找到最大元素的max。
        此外还有在必要时再把它们转换回对象流的方法。要记住的是，这些特化的原因并不在于流的复杂性，
        而是装箱造成的复杂性——即类似int和Integer之间的效率差异。

        映射到数值流
            将流转换为特化版本的常用方法是mapToInt、mapToDouble和mapToLong。
     */
    public static void demo1(){
    
        List<Integer> numbers = Arrays.asList(3,4,5,1,2);
        Arrays.stream(numbers.toArray()).forEach(System.out::println);
        /*
        这里，mapToInt会从每道菜中提取热量（用一个Integer表示），并返回一个IntStream
        （而不是一个Stream<Integer>）。然后你就可以调用IntStream接口中定义的sum方法，对卡
        路里求和了！请注意，如果流是空的，sum默认返回0。IntStream还支持其他的方便方法，如
        max、min、average等。
         */
        //映射到数值流
        int calories = menu.stream()
                           .mapToInt(Dish::getCalories)
                           .sum();
        System.out.println("Number of calories:" + calories);

        //转换为对象流
        IntStream intStream = menu.stream().mapToInt(Dish::getCalories);//将Stream转 换为数值流
        Stream<Integer> stream = intStream.boxed();//将数值流转换为Stream

        //默认值OptionalInt       max and OptionalInt
        //Optional类，这是一个可以表示值存在或不存在的容器。
        //Optional可以用Integer、String等参考类型来参数化。对于三种原始流特化，也分别有一个Optional原始类
        //型特化版本：OptionalInt、OptionalDouble和OptionalLong。

        OptionalInt maxCalories = menu.stream()                                                      
                                      .mapToInt(Dish::getCalories)
                                      .max();
        //方式1
        int max;
        if(maxCalories.isPresent()){
            max = maxCalories.getAsInt();
        }
        else {
            // we can choose a default value
            max = 1;
        }
        System.out.println(max);
        //方式2    如果没有最大值的话，你就可以显式处理OptionalInt去定义一个默认值了：
        int max1 = maxCalories.orElse(1);
        System.out.println(max1);


        //数值范围：和数字打交道时，有一个常用的东西就是数值范围。比如，假设你想要生成1和100之间的所有数字。
        //Java 8引入了两个可以用于IntStream和LongStream的静态方法，帮助生成这种范围：range和rangeClosed。
        //这两个方法都是第一个参数接受起始值，第二个参数接受结束值。但range是不包含结束值的，而rangeClosed则包含结束值。
        // numeric ranges
        IntStream evenNumbers = IntStream.rangeClosed(1, 100)      //表示范围[1, 100]
                                 .filter(n -> n % 2 == 0);         //一个从1到 100的偶数 流
        System.out.println(evenNumbers.count());                  //从1到100有 50个偶数
        //如果改用 IntStream.range(1, 100)，则结果将会是49个偶数，因为range是不包含结束值的。


        //数值流应用：勾股数
        //Math.sqrt(a * a + b * b) % 1 == 0这一行是怎么
        //回事。简单来说，这是一种测试Math.sqrt(a * a + b * b)返回的结果是不是整数的方法。
       // 如果平方根的结果带了小数，如9.1，这个条件就不成立（9.0是可以的）。
        Stream<int[]> pythagoreanTriples =
               IntStream.rangeClosed(1, 100).boxed()
                        .flatMap(a -> IntStream.rangeClosed(a, 100)
                                               .filter(b -> Math.sqrt(a*a + b*b) % 1 == 0).boxed()
                                               .map(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)}));       

        pythagoreanTriples.forEach(t -> System.out.println(t[0] + ", " + t[1] + ", " + t[2]));

        Stream<double[]> pythagoreanTriples2 =
                IntStream.rangeClosed(1, 100).boxed()
                        .flatMap(a ->
                                IntStream.rangeClosed(a, 100)
                                        .mapToObj(b -> new double[]{a, b, Math.sqrt(a*a + b*b)})
                                        .filter(t -> t[2] % 1 == 0));       //元组中的第三个元素必 须是整数


    }
   
    public static boolean isPerfectSquare(int n){
        return Math.sqrt(n) % 1 == 0;
    }

}
